package stripsPlanner;

import java.util.ArrayList;
import java.util.List;

import exceptions.RevertException;
import exceptions.NoResolveException;
import exceptions.NoSubgoalException;
import exceptions.PredicateException;

import stripsLanguage.Operator;
import stripsLanguage.Plan;
import stripsLanguage.Predicate;
import stripsLanguage.Step;
import stripsLanguage.Link;

/**
 * Algorytm POP
 * 
 * @author Piotr Demidowicz
 *
 */

public class PartialOrderPlanner {

	public PartialOrderPlanner() {}
	
	public static final int MaxNrOfSteps = 2 + 15;
	
	public List<DataStorage> POP(Plan firstPlan, List<Operator> operators)
		throws NoSubgoalException, PredicateException
	{				
		Plan plan = firstPlan.copyPlan(operators);
		List<DataStorage> DSs = new ArrayList<DataStorage>(); // lista planow
		Subgoal subgoal = null;
		StoredChoosing storedChoosing = null;
		
		DSs.add(
			new DataStorage(
				firstPlan,
				null,
				subgoal,
				0,
				-1));
			
		long startTime;// = System.currentTimeMillis();
		long endTime;
		long intervalSelect = 0;
		long intervalChoose = 0;
		long intervalResolve = 0;
		long intervalFailure = 0;
		long intervalWriteToOutput = 0;
		long allTime = 0;
		long allTimeT1 = 0;
		int idOfPreviousDS = 0;	
		boolean previousPlan = false;
		int iter = 0;
		while(!checkIfSolutionFound(plan.getSteps(),plan.getLinks()))
		{
			++iter;
			if(iter % 100 == 0)
				System.out.println("Iteracja: " + iter);
			//startTime = System.currentTimeMillis();
			//System.out.println("Iteracja nr " + iter);
			//endTime = System.currentTimeMillis();
			//intervalWriteToOutput += endTime - startTime;
			allTimeT1 = System.currentTimeMillis();
			if(!previousPlan)
			{
				startTime = System.currentTimeMillis();			
				subgoal = new SelectSubgoal().selectSubgoal(
					plan.getSteps(),
					plan.getLinks());
				endTime = System.currentTimeMillis();
				intervalSelect += endTime - startTime;
			}
		//	System.out.println("Subgoal do spelnienia: " + subgoal.getPredicateOfSubgoal().toString(false)
		//		+ ", w kroku: (" + subgoal.getStepOfSubgoal().getName() + ", "
		//		+ subgoal.getStepOfSubgoal().getId() + ")");
			
			try {
				startTime = System.currentTimeMillis();	
				if(!previousPlan)
				{
					storedChoosing = null;	
					storedChoosing = new ChooseOperator().chooseOperator(plan, operators, subgoal, storedChoosing);
					idOfPreviousDS = DSs.size() - 1;
				} else						
					storedChoosing = new ChooseOperator().chooseOperator(plan, operators, subgoal, storedChoosing);				
				endTime = System.currentTimeMillis();
				intervalChoose += endTime - startTime;
				
				previousPlan = false;
				if(MaxNrOfSteps < plan.getSteps().size())
					//tego co dodal usunac
					throw new RevertException("Za duzo krokow");				
				//	System.out.println("Dodaje DS: "+ DSs.size() + ", ktorego poprzednikiem jest: " + idOfPreviousDS);
				DSs.add(
					new DataStorage(
						plan,
						storedChoosing.copyChoosing(),
						subgoal,
						DSs.size(),
						idOfPreviousDS));	
				plan = plan.copyPlan(operators);
				startTime = System.currentTimeMillis();
				new ResolveThreats().resolveThreats(plan);
				endTime = System.currentTimeMillis();
				intervalResolve += endTime - startTime;
			} catch (RevertException exc)
			{
			//	System.out.println("NoOperator");
				previousPlan = true;
				
				startTime = System.currentTimeMillis();
				ResultsFailure rf = failure(						
					DSs,					
					operators,
					true);
				endTime = System.currentTimeMillis();
				intervalFailure += endTime - startTime;
				
				idOfPreviousDS = rf.getIdOfDS();
				plan = rf.getPlan();
				subgoal = rf.getSubgoal();
				storedChoosing = rf.getStoredChoosing();
			} catch (NoResolveException e)
			{
				DSs.get(DSs.size()-1).setWasHere(); //ostatni jest zly
				//System.out.println("ResolveThreats");
				previousPlan = true;
				
				startTime = System.currentTimeMillis();				
				ResultsFailure rf = failure(						
					DSs,								
					operators,
					false);
				endTime = System.currentTimeMillis();
				intervalFailure += endTime - startTime;
				
				idOfPreviousDS = rf.getIdOfDS();
				plan = rf.getPlan();
				subgoal = rf.getSubgoal();
				storedChoosing = rf.getStoredChoosing();							
			}
			allTime += (System.currentTimeMillis() - allTimeT1);
			//System.out.println(plan.toString());
			//System.out.println(storedChoosing.toString());		
		}

		System.out.println("Choose: " + intervalChoose + "\nSelect: " + intervalSelect
			+ "\nResolve: " + intervalResolve + "\nFailure: " + intervalFailure
			+ "\nOutput: " + intervalWriteToOutput
			+ "\nTe wymienione razem: " 
			+ (intervalChoose + intervalSelect + intervalResolve + intervalFailure + intervalWriteToOutput));
		System.out.println("Liczba iteracji: " + iter);
		System.out.println(MyTimeClass.getInstance().toString());
		
		
		// dodaje ostatni - dobry, plan
		DSs.add(
			new DataStorage(
				plan,
				null,
				null,
				-2,
				-2));
		
		return DSs;
	}
	
	/**
	 * liczy linki, liczy preconditions i jak tyle samo, to koniec
	 * zwraca true, jesli nie ma predykatu bez Linka
	 * @param plan
	 * @return
	 * TEST niepotrzebny
	 */
	private boolean checkIfSolutionFound(List<Step> steps, List<Link> links)
	{
		// lepiej policzyc precondition i policzyc linki - musi sie liczba zgadzac
		int nrOfPreconditions = 0;
		for(Step step: steps)
			nrOfPreconditions += step.getPreconditions().size();
		if(nrOfPreconditions == links.size())
			return true;
		return false;
	}
	
	/**
	 * zwraca subgoal nakierowany na krok (po id) i predykat (po id)
	 * @param subgoal
	 * @param steps
	 * @return
	 * @throws NoSubgoalException
	 * TEST
	 */
	protected Subgoal getSameSubgoal(Subgoal subgoal, List<Step> steps) throws NoSubgoalException
	{
		Step s = steps.get(subgoal.getStepOfSubgoal().getId()-1);			
		for(Predicate statePre: s.getPreconditions())				
			if(statePre.getIdOfPredicate() == subgoal.getPredicateOfSubgoal().getIdOfPredicate())					
				return new Subgoal(s,statePre);
		throw new NoSubgoalException();
	}
	
	protected ResultsFailure failure(
		List<DataStorage> plansAndChoosing,		
		List<Operator> operators,
		boolean setWasHere) throws NoSubgoalException
	{
		DataStorage dS = null;
		DataStorage subgoalDS = null;
				
		// pobierz plan, do ktorego sie cofniesz
		for(int i = plansAndChoosing.size()-1; i > 0; --i)
		{
			dS = plansAndChoosing.get(i);
			if(!dS.isWasHere())
				break;
		}
		if(setWasHere) // jesli przebadano calkowicie jakas opcje
		{
			dS.setWasHere();
			//DataStorage ddd = dS;
			dS = plansAndChoosing.get(dS.getIdOfPreviousDataStorage());
			//plansAndChoosing.remove(ddd.getIdOfDataStorage());
		}
		// pobierz zadanie do wykonania
		for(int i = plansAndChoosing.size()-1; i > 0; --i)
		{
			subgoalDS = plansAndChoosing.get(i);
			if(subgoalDS.getIdOfPreviousDataStorage() == dS.getIdOfDataStorage())
				break;
		}
		
		//System.out.println("Nawrot do: " + dS.getIdOfDataStorage() + ", z celem z : " + subgoalDS.getIdOfDataStorage());
		
		/***
		 * usuwa te spomiedzy subgoalDS, a dS
		 */
		for(int i = subgoalDS.getIdOfDataStorage() - 1 ; i > dS.getIdOfDataStorage() ; --i)
			if(plansAndChoosing.get(i).getIdOfPreviousDataStorage() == dS.getIdOfDataStorage())
				plansAndChoosing.remove(i);
		
		
		Plan plan = dS.getPlan().copyPlan(operators); 		// pobranie aktualnego planu
		return new ResultsFailure(
			dS.getIdOfDataStorage(),
			plan,
			getSameSubgoal(
				subgoalDS.getSubgoal(),
				plan.getSteps()),
			subgoalDS.getStoredChoosing());	
		}
}
